#!/usr/bin/env python
# -*- coding: utf-8 -*-

from typing import Any

from cta.config.cross_section_config.multi_factor_config import MultiFactorConfig
from cta.interface.action.overweight.abstract_overweight_action import AbstractOverweightAction
from web.constants.datetime_format import DatetimeFormat
from web.constants.direction import Direction
from web.domain.responsibility_chain_dto import ResponsibilityChainDto
from web.manager.log_manager import LogManager
from web.models import CommodityFutureInfo
from web.models.commodity_future_date_contract_data import CommodityFutureDateContractData
from web.models.optimize2_account import Optimize2Account
from web.models.optimize2_commodity_future_transaction_record import Optimize2CommodityFutureTransactionRecord
from web.models.quant2_account_log import Quant2AccountLog
from web.models.quant2_commodity_future_transaction_record import Quant2CommodityFutureTransactionRecord
from web.util.commodity_future_code_util import CommodityFutureCodeUtil
from web.util.datetime_util import DatetimeUtil
from web.util.fee_util import FeeUtil

Logger = LogManager.get_logger(__name__)

class Optimize2MultiFactorOverweightAction(AbstractOverweightAction):
    """
    多因子策略，加仓期货
    """

    def exec(self, responsibility_chain_dto: ResponsibilityChainDto = None) -> Any:
        Logger.info("多因子策略，加仓期货")

        transaction_date: str = responsibility_chain_dto.transaction_date

        # 根据持仓时间，判断k线形态；判断当天是否应当平仓和开仓。只有在非开仓和平仓时间才减仓
        k_line_movement_pattern, close_or_open_position = self.optimize2_k_line_movement_pattern_and_close_or_open_position_by_holding_position_time(transaction_date)
        if close_or_open_position:
            # if not self.close_or_open_position_by_up_down_percentage_with_n_date(transaction_date):
            Logger.info("日期[%s]应当平仓和开仓，不可以减仓或加仓", transaction_date)
            responsibility_chain_dto.has_overweight = False
            super().next(responsibility_chain_dto)
            return

        # 根据日期查询记录
        quant2_account_log_list: list[Quant2AccountLog] = self.quant2_account_log_dao.find_by_date(transaction_date)
        if quant2_account_log_list is not None and len(quant2_account_log_list) > 0:
            for quant2_account_log in quant2_account_log_list:
                optimize2_account: Optimize2Account = self.optimize2_account_dao.find_by_account_name(quant2_account_log.account_name)
                # 如果对应账号的仓位已满，则不用再加仓
                if optimize2_account.hold_commodity_future_number == MultiFactorConfig.Max_Hold_Commodity_Future_Number:
                    continue

                # 表示牛熊线
                bull_short_line: float = None
                # 是否在牛熊线之下
                below_bull_short_line: bool = False
                if not below_bull_short_line and quant2_account_log.bias250 is not None and quant2_account_log.bias250 <= MultiFactorConfig.Bias_Threshold_Top and quant2_account_log.bias250 >= MultiFactorConfig.Bias_Threshold_Bottom:
                    bull_short_line = quant2_account_log.ma250
                    if quant2_account_log.total_assets >= bull_short_line:
                        below_bull_short_line = True
                if not below_bull_short_line and quant2_account_log.bias120 is not None and quant2_account_log.bias120 <= MultiFactorConfig.Bias_Threshold_Top and quant2_account_log.bias120 >= MultiFactorConfig.Bias_Threshold_Bottom:
                    bull_short_line = quant2_account_log.ma120
                    if quant2_account_log.total_assets >= bull_short_line:
                        below_bull_short_line = True
                if not below_bull_short_line and quant2_account_log.bias60 is not None and quant2_account_log.bias60 <= MultiFactorConfig.Bias_Threshold_Top and quant2_account_log.bias60 >= MultiFactorConfig.Bias_Threshold_Bottom:
                    bull_short_line = quant2_account_log.ma60
                    if quant2_account_log.total_assets >= bull_short_line:
                        below_bull_short_line = True
                if not below_bull_short_line and quant2_account_log.bias20 is not None and quant2_account_log.bias20 <= MultiFactorConfig.Bias_Threshold_Top and quant2_account_log.bias20 >= MultiFactorConfig.Bias_Threshold_Bottom:
                    bull_short_line = quant2_account_log.ma20
                    if quant2_account_log.total_assets >= bull_short_line:
                        below_bull_short_line = True
                if not below_bull_short_line and quant2_account_log.bias10 is not None and quant2_account_log.bias10 <= MultiFactorConfig.Bias_Threshold_Top and quant2_account_log.bias10 >= MultiFactorConfig.Bias_Threshold_Bottom:
                    bull_short_line = quant2_account_log.ma10
                    if quant2_account_log.total_assets >= bull_short_line:
                        below_bull_short_line = True
                if not below_bull_short_line and quant2_account_log.bias5 is not None and quant2_account_log.bias5 <= MultiFactorConfig.Bias_Threshold_Top and quant2_account_log.bias5 >= MultiFactorConfig.Bias_Threshold_Bottom:
                    bull_short_line = quant2_account_log.ma5
                    if quant2_account_log.total_assets >= bull_short_line:
                        below_bull_short_line = True
                # 如果最后任何bias指标都无法确定哪一根均线可以作为牛熊线的话，则将前一个交易日的总资产作为牛熊线
                last_quant2_account_log: Quant2AccountLog = self.quant2_account_log_dao.find_last_by_account_name_and_date(quant2_account_log.account_name, transaction_date)
                bull_short_line = last_quant2_account_log.total_assets
                if quant2_account_log.total_assets >= bull_short_line:
                    below_bull_short_line = True

                # 加仓
                if below_bull_short_line:
                    # 每次仓位增减数量
                    overweight_or_underweight_amount: int = MultiFactorConfig.Overweight_Or_Underweight_Amount
                    # 仓位
                    hold_commodity_future_number: int = optimize2_account.hold_commodity_future_number
                    # 查询可以加仓的期货
                    quant2_commodity_future_transaction_record_list: list[Quant2CommodityFutureTransactionRecord] = self.quant2_commodity_future_transaction_record_dao.find_by_account_name_and_hold_in_quant2_commodity_future_transaction_record_and_not_hold_in_optimize2_commodity_future_transaction_record(optimize2_account.account_name, transaction_date)
                    if quant2_commodity_future_transaction_record_list is not None and len(quant2_commodity_future_transaction_record_list) > 0:
                        for quant2_commodity_future_transaction_record in quant2_commodity_future_transaction_record_list:
                            if overweight_or_underweight_amount > 0 and hold_commodity_future_number < MultiFactorConfig.Max_Hold_Commodity_Future_Number:
                                # 查询期货记录
                                commodity_future_date_contract_data: CommodityFutureDateContractData = self.commodity_future_date_contract_data_dao.find_by_code_and_transaction_date(quant2_commodity_future_transaction_record.code, transaction_date)
                                if commodity_future_date_contract_data is None:
                                    Logger.warning("期货[%s]在日期[%s]没有交易记录，跳过这只期货", optimize2_account.account_name, transaction_date)
                                    continue

                                # 查询commodity_future_info记录（大小写不敏感的精确匹配查询）
                                filter_dict = {'code__iexact': CommodityFutureCodeUtil.code_to_contract_code(quant2_commodity_future_transaction_record.code)}
                                commodity_future_info: CommodityFutureInfo = self.commodity_future_info_dao.find_one(filter_dict, dict(), list())

                                # 如果可以开仓，则向optimize2_c_f_transact_record表中插入数据。
                                # 计算应该开仓期货的数量

                                # 应当开仓的期货的数量
                                should_open_position_commodity_future_number: int = -1
                                if len(quant2_commodity_future_transaction_record_list) <= (MultiFactorConfig.Max_Hold_Commodity_Future_Number - optimize2_account.hold_commodity_future_number):
                                    should_open_position_commodity_future_number = len(quant2_commodity_future_transaction_record_list)
                                else:
                                    should_open_position_commodity_future_number = MultiFactorConfig.Max_Hold_Commodity_Future_Number - optimize2_account.hold_commodity_future_number

                                # 判断是否需要继续开仓
                                if should_open_position_commodity_future_number == 0:
                                    Logger.info("账号[%s]持有期货的数量已经是[%d]，不再需要开仓期货", optimize2_account.account_name, MultiFactorConfig.Max_Hold_Commodity_Future_Number)
                                    break

                                # 计算开仓多少
                                # 买入多少
                                init_buy_or_sell_lot: int = FeeUtil.calculate_open_commodity_future_lot(commodity_future_date_contract_data.close_price, commodity_future_info, float(optimize2_account.capital_assets) / float(MultiFactorConfig.Max_Hold_Commodity_Future_Number - optimize2_account.hold_commodity_future_number))
                                buy_or_sell_lot: int = init_buy_or_sell_lot if init_buy_or_sell_lot > 1 else 1
                                while float(optimize2_account.capital_assets) / float(MultiFactorConfig.Max_Hold_Commodity_Future_Number - optimize2_account.hold_commodity_future_number) >= FeeUtil.calculate_open_commodity_future_cost_and_fee(commodity_future_date_contract_data.close_price, commodity_future_info, buy_or_sell_lot):
                                    if FeeUtil.calculate_open_commodity_future_cost_and_fee(commodity_future_date_contract_data.close_price, commodity_future_info, buy_or_sell_lot) >= float(optimize2_account.capital_assets) / float(MultiFactorConfig.Max_Hold_Commodity_Future_Number - optimize2_account.hold_commodity_future_number):
                                        break
                                    buy_or_sell_lot = buy_or_sell_lot + 1
                                if float(optimize2_account.capital_assets) / float(MultiFactorConfig.Max_Hold_Commodity_Future_Number - optimize2_account.hold_commodity_future_number) < FeeUtil.calculate_open_commodity_future_cost_and_fee(commodity_future_date_contract_data.close_price, commodity_future_info, buy_or_sell_lot):
                                    if buy_or_sell_lot == 1:
                                        continue
                                else:
                                    buy_or_sell_lot = buy_or_sell_lot - 1

                                # 加仓
                                optimize2_commodity_future_transaction_record: Optimize2CommodityFutureTransactionRecord = Optimize2CommodityFutureTransactionRecord()
                                optimize2_commodity_future_transaction_record.code = quant2_commodity_future_transaction_record.code
                                optimize2_commodity_future_transaction_record.account_name = quant2_commodity_future_transaction_record.account_name
                                optimize2_commodity_future_transaction_record.direction = quant2_commodity_future_transaction_record.direction
                                optimize2_commodity_future_transaction_record.k_line_movement_pattern = k_line_movement_pattern
                                if quant2_commodity_future_transaction_record.direction == Direction.Up:
                                    optimize2_commodity_future_transaction_record.buy_lot = buy_or_sell_lot
                                    optimize2_commodity_future_transaction_record.buy_date = DatetimeUtil.str_to_datetime(transaction_date, DatetimeFormat.Date_Format)
                                    optimize2_commodity_future_transaction_record.buy_price = quant2_commodity_future_transaction_record.buy_price
                                    optimize2_commodity_future_transaction_record.open_commission = FeeUtil.calculate_open_commodity_future_fee(quant2_commodity_future_transaction_record.buy_price, commodity_future_info, buy_or_sell_lot)
                                if quant2_commodity_future_transaction_record.direction == Direction.Down:
                                    optimize2_commodity_future_transaction_record.sell_lot = buy_or_sell_lot
                                    optimize2_commodity_future_transaction_record.sell_date = DatetimeUtil.str_to_datetime(transaction_date, DatetimeFormat.Date_Format)
                                    optimize2_commodity_future_transaction_record.sell_price = quant2_commodity_future_transaction_record.sell_price
                                    optimize2_commodity_future_transaction_record.open_commission = FeeUtil.calculate_open_commodity_future_fee(quant2_commodity_future_transaction_record.sell_price, commodity_future_info, buy_or_sell_lot)
                                self.optimize2_commodity_future_transaction_record_dao.save(optimize2_commodity_future_transaction_record)

                                overweight_or_underweight_amount = overweight_or_underweight_amount - 1
                                hold_commodity_future_number = hold_commodity_future_number + 1
        else:
            Logger.warning("表quant2_account_log中没有日期为[%s]的记录", transaction_date)

        responsibility_chain_dto.has_overweight = True
        super().next(responsibility_chain_dto)